package de.koller.so.render.proto;

import de.koller.oceanFramework.gl.util.ImagePreview;
import de.koller.oceanFramework.math.IntersectionUtil;
import de.koller.oceanFramework.math.Vec3;
import de.koller.oceanFramework.util.ColorIntUtil;
import java.awt.image.BufferedImage;
import java.util.Random;

public class Sphere2D {

	private final static Random rnd = new Random();
	
	private final Point sphereOrigin = new Point( 300, 300, 255 );
	private final float sphereRadius = 255;
	private final Point lightVec;

	public Sphere2D( float x, float y, float z ) {
		lightVec = new Point( x, y, z );
	}
	
	private BufferedImage genBI() {
		BufferedImage bi = new BufferedImage( 600, 600, BufferedImage.TYPE_INT_ARGB );
		return bi;
	}
	
	public BufferedImage generatePic() {
		BufferedImage bi = genBI();
		for( int x = 0; x < bi.getWidth(); x++ )
			for( int y = 0; y < bi.getHeight(); y++ )
				bi.setRGB( x, y, handleColor( x, y ) );
		return bi;
	}
	
	private int handleColor( int x, int y ) {
		Point p = new Point();
		if( isIntersect( x, y, p ) ) {
			Vec3 n = new Vec3( sphereOrigin.x-x, sphereOrigin.y-y, sphereOrigin.z-p.z );
			n = n.normalize();

			Vec3 ln = new Vec3( lightVec.x, lightVec.y, lightVec.z );
			ln = ln.normalize();
			
			float a = (float)ln.angleBetween( n );
			if( a > Math.PI / 2f )
				return 0xff000000;
			
			int c = 0xff - (int)(a / (float)(Math.PI / 2f) * 255f);
			int ambientC = ColorIntUtil.getIntColor( 0xff, c, c, c );
			
			//specular
			Vec3 h = new Vec3( ln.getX(), ln.getY(), ln.getZ() ).add( new Vec3( 0, 0, 1 ) ).normalize();
			double specular = Math.pow( Math.max( 0, n.dot( h ) ), 350 );
			
			int sr = ColorIntUtil.clampSegment( (int)(specular * 0xff) );
			int sg = ColorIntUtil.clampSegment( (int)(specular * 0xff) );
			int sb = ColorIntUtil.clampSegment( (int)(specular * 0xff) );

			return ColorIntUtil.add( ambientC, ColorIntUtil.getIntColor( 0xff, sr, sg, sb ) );
			//return ambientC;
		} else
			return 0xff000000;
	}
	
	private boolean isIntersect( int x, int y, Point intersection ) {
		intersection.x = x;
		intersection.y = y;
		
		Point rayOrigin = new Point( x - sphereOrigin.x, y - sphereOrigin.y, 0 - sphereOrigin.z );
		Point rayDirection = new Point( 0, 0, 1 );
		
		float a = dot( rayDirection, rayDirection );
		float b = 2 * dot( rayDirection, rayOrigin );
		float c = dot( rayOrigin, rayOrigin ) - ( sphereRadius * sphereRadius );
		
		float disc = b * b - 4 * a * c;
		if( disc < 0 )
			return false;

		float distSqrt = (float)Math.sqrt( disc );
		float q;
		if( b < 0 )
			q = ( -b - distSqrt ) / 2f;
		else
			q = ( -b + distSqrt ) / 2f;

		float t0 = q / a;
		float t1 = c / q;

		if (t0 > t1) {
			float temp = t0;
			t0 = t1;
			t1 = temp;
		}

		if( t1 < 0 )
			return false;

		if( t0 < 0 ) {
			intersection.z = t1;
			return true;
		} else {
			intersection.z = t0;
			return true;
		}
		
	}
	
	private static float dot( Point p1, Point p2 ) {
		Vec3 p1v = new Vec3( p1.x, p1.y, p1.z );
		Vec3 p2v = new Vec3( p2.x, p2.y, p2.z );
		return (float)IntersectionUtil.dot( p1v, p2v );
	}
	
	private static class Point {
		private float x, y, z;

		private Point() {}
		
		private Point( float x, float y, float z ) {
			this.x = x;
			this.y = y;
			this.z = z;
		}
		
		private void norm() {
			float l = (float)Math.sqrt( x*x + y*y + z*z );
			x = x / l;
			y = y / l;
			z = z / l;
		}
	}
	
	public static void main( String[] args ) {
		//for( float x = -2; x < 2; x += 0.1f )
			ImagePreview.showImage( new Sphere2D( .5f, .5f, 0 ).generatePic(), 600, 600 );
	}
	
}
